package day13;

import java.util.LinkedList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author: 王丹
 * @create: 2021-07-06 10:51
 **/
//使用两个Condition来实现一个阻塞队列
public class BlockingQueueDemo<E> {
    int size;//阻塞队列最大容量
    ReentrantLock lock = new ReentrantLock();
    LinkedList<E> list = new LinkedList();
    Condition notFull = lock.newCondition();
    Condition notEmpty = lock.newCondition();

    public BlockingQueueDemo(int size) {
        this.size = size;
    }

    //入队列
    public void enqueue(E e) throws InterruptedException {
        lock.lock();
        try {
            while (list.size() == size) {//队列已满,在notFull条件上等待
                notFull.await();
            }
            list.add(e);//入队:加入链表末尾
            System.out.println("入队：" + e);
            notEmpty.signal();
        } finally {
            lock.unlock();
        }
    }

    //出队列
    public E dequeue() throws InterruptedException {
        E e;
        lock.lock();
        try {
            while (list.size() == 0) {
                notEmpty.await();
            }
            e = list.removeFirst();
            System.out.println("出队：" + e);
            notFull.signal();
            return e;
        } finally {
            lock.unlock();
        }
    }

    /*
    结果分析：
    代码非常容易理解，创建了一个阻塞队列，大小为3，队列满的时候，会被阻塞，等待其他线程去消费，
    队列中的元素被消费之后，会唤醒生产者，生产数据进入队列。上面代码将队列大小置为1，
    可以实现同步阻塞队列，生产1个元素之后，生产者会被阻塞，待消费者消费队列中的元素之后，
    生产者才能继续工作。
     */
    public static void main(String[] args) {
        BlockingQueueDemo<Integer> queue = new BlockingQueueDemo(1);
        for (int i = 0; i < 10; ++i) {
            int data = i;
            new Thread(new Runnable() {
                public void run() {
                    try {
                        queue.enqueue(data);
                    } catch (InterruptedException e) {
                    }
                }
            }).start();
        }

        for (int i = 0; i < 10; ++i) {
            new Thread(new Runnable() {
                public void run() {
                    try {
                        Integer data = queue.dequeue();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
            }).start();
        }

    }
}
